package com.yycx.common.security;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.yycx.common.configuration.OpenCommonProperties;
import com.yycx.common.base.utils.FlymeUtils;
import com.yycx.common.utils.ReflectionUtils;
import com.yycx.common.utils.SpringContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import org.springframework.util.Assert;

import javax.sql.DataSource;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;

/**
 * 获取认证信息
 *
 * @author zyf
 */
@Slf4j
public class OpenHelper {

    /**
     * 获取认证用户信息
     *
     * @return
     */
    public static OpenUser getUser() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

        if (authentication != null && authentication.isAuthenticated() && authentication instanceof OAuth2Authentication) {
            OAuth2Authentication oAuth2Authentication = (OAuth2Authentication) authentication;
            OAuth2Request clientToken = oAuth2Authentication.getOAuth2Request();
            if (!oAuth2Authentication.isClientOnly()) {
                if (authentication.getPrincipal() instanceof OpenUser) {
                    OpenUser openUser1 = (OpenUser) authentication.getPrincipal();
                    openUser1.setClientId(clientToken.getClientId());
                    return openUser1;
                }
                if (authentication.getPrincipal() instanceof Map) {
                    return BeanUtil.mapToBean((Map) authentication.getPrincipal(), OpenUser.class, false);
                }
            } else {
                OpenUser openUser = new OpenUser();
                openUser.setClientId(clientToken.getClientId());
                openUser.setAuthorities(clientToken.getAuthorities());
                return openUser;
            }
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }
        return null;
    }

    /**
     * 获取认证用户Id
     *
     * @return
     */
    public static Long getUserId() {
        OpenUser openUser = getUser();
        if (FlymeUtils.isNotEmpty(openUser)) {
            return openUser.getUserId();
        }
        return null;
    }

    /**
     * 获取认证用户Id
     *
     * @return
     */
    public static String getStrUserId() {
        OpenUser openUser = getUser();
        if (FlymeUtils.isNotEmpty(openUser)) {
            return openUser.getUserId().toString();
        }
        return null;
    }

    /**
     * 获取认证用户名
     *
     * @return
     */
    public static String getUserName() {
        OpenUser openUser = getUser();
        if (FlymeUtils.isNotEmpty(openUser)) {
            return openUser.getUsername();
        }
        return "";
    }

    /**
     * 获取企业ID
     *
     * @return
     */
    public static Long getCompanyId() {
        OpenUser openUser = getUser();
        if (FlymeUtils.isNotEmpty(openUser)) {
            return openUser.getCompanyId();
        }
        return null;
    }

    /**
     * 获取机构ID
     *
     * @return
     */
    public static Long getOrganizationId() {
        OpenUser openUser = getUser();
        if (FlymeUtils.isNotEmpty(openUser)) {
            return openUser.getOrganizationId();
        }
        return null;
    }

    /**
     * 获取认证账户Id
     *
     * @return
     */
    public static Long getAccountId() {
        OpenUser openUser = getUser();
        if (FlymeUtils.isNotEmpty(openUser)) {
            return openUser.getAccountId();
        }
        return null;
    }

    /**
     * 是否拥有权限
     *
     * @param authority
     * @return
     */
    public static Boolean hasAuthority(String authority) {
        OpenUser auth = getUser();
        if (auth == null) {
            return false;
        }
        if (AuthorityUtils.authorityListToSet(auth.getAuthorities()).contains(authority)) {
            return true;
        }
        return false;
    }

    /**
     * 判断当前用户是否拥有某个角色
     */
    public static Boolean hasRole(String... roleCodes) {
        Boolean tag = false;
        if (ObjectUtils.isNotEmpty(roleCodes)) {
            for (String roleCode : roleCodes) {
                Boolean hasRole = hasAuthority(SecurityConstants.AUTHORITY_PREFIX_ROLE + roleCode);
                if (hasRole) {
                    tag = true;
                    break;
                }
            }
        }
        return tag;
    }


    /**
     * 更新OpenUser
     *
     * @param openUser
     */
    public static void updateOpenUser(TokenStore tokenStore, OpenUser openUser) {
        if (openUser == null) {
            return;
        }
        Assert.notNull(openUser.getClientId(), "客户端ID不能为空");
        Assert.notNull(openUser.getUsername(), "用户名不能为空");
        // 动态更新客户端生成的token
        Collection<OAuth2AccessToken> accessTokens = tokenStore.findTokensByClientIdAndUserName(openUser.getClientId(), openUser.getUsername());
        if (accessTokens != null && !accessTokens.isEmpty()) {
            for (OAuth2AccessToken accessToken : accessTokens) {
                // 由于没有set方法,使用反射机制强制赋值
                OAuth2Authentication oAuth2Authentication = tokenStore.readAuthentication(accessToken);
                if (oAuth2Authentication != null) {
                    Authentication authentication = oAuth2Authentication.getUserAuthentication();
                    ReflectionUtils.setFieldValue(authentication, "principal", openUser);
                    // 重新保存
                    OpenTokenEnhancer openTokenEnhancer = new OpenTokenEnhancer();
                    OAuth2AccessToken auth2AccessToken = openTokenEnhancer.enhance(accessToken, oAuth2Authentication);
                    tokenStore.storeAccessToken(auth2AccessToken, oAuth2Authentication);
                }
            }
        }
    }


    /***
     * 更新客户端权限
     * @param tokenStore
     * @param clientId
     * @param authorities
     */
    public static void updateOpenClientAuthorities(TokenStore tokenStore, String clientId, Collection<? extends GrantedAuthority> authorities) {
        if (authorities == null) {
            return;
        }
        // 动态更新客户端生成的token
        Collection<OAuth2AccessToken> accessTokens = tokenStore.findTokensByClientId(clientId);
        if (accessTokens != null && !accessTokens.isEmpty()) {
            Iterator<OAuth2AccessToken> iterator = accessTokens.iterator();
            while (iterator.hasNext()) {
                OAuth2AccessToken token = iterator.next();
                OAuth2Authentication oAuth2Authentication = tokenStore.readAuthentication(token);
                if (oAuth2Authentication != null && oAuth2Authentication.isClientOnly()) {
                    // 只更新客户端权限
                    // 由于没有set方法,使用反射机制强制赋值
                    ReflectionUtils.setFieldValue(oAuth2Authentication, "authorities", authorities);
                    // 重新保存
                    tokenStore.storeAccessToken(token, oAuth2Authentication);
                }
            }
        }
    }

    /**
     * 认证服务器原始方式创建AccessToken
     *
     * @param endpoints
     * @param postParameters
     * @return
     * @throws Exception
     */
    public static OAuth2AccessToken createAccessToken(AuthorizationServerEndpointsConfiguration endpoints, Map<String, String> postParameters) throws Exception {
        String clientId = postParameters.get("client_id");
        String clientSecret = postParameters.get("client_secret");
        Assert.notNull(clientId, "客户端ID不能为空");
        Assert.notNull(clientSecret, "客户端秘钥不能为空");
        clientId = clientId.trim();
        ClientDetailsService clientDetailsService = (ClientDetailsService) ReflectionUtils.getFieldValue(endpoints, "clientDetailsService");
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        // 验证客户端
        ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);
        if (clientDetails == null) {
            throw new NoSuchClientException("No client with requested id:" + clientId);
        }
        if (!passwordEncoder.matches(clientSecret, clientDetails.getClientSecret())) {
            throw new InvalidClientException("Bad client credentials");
        }
        UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(clientId, clientSecret, Collections.emptyList());
        ResponseEntity<OAuth2AccessToken> responseEntity = endpoints.tokenEndpoint().postAccessToken(auth, postParameters);
        return responseEntity.getBody();
    }

}
